package cn.bfay.lion.jingdong.service.impl;

import cn.bfay.cache.redis.RedisKeyBuilder;
import cn.bfay.cache.redis.RedisTime;
import cn.bfay.cache.redis.RedisUtils;
import cn.bfay.lion.jingdong.client.rsp.JdProductInfo;
import cn.bfay.lion.jingdong.entity.ProductDetailInfo;
import cn.bfay.lion.jingdong.entity.ProductInfo;
import cn.bfay.lion.jingdong.mapper.ProductDetailInfoMapper;
import cn.bfay.lion.jingdong.mapper.ProductInfoMapper;
import cn.bfay.lion.jingdong.mq.NoticeProducer;
import cn.bfay.lion.jingdong.redis.RedisKeyTemplate;
import cn.bfay.lion.jingdong.service.AbstractService;
import cn.bfay.lion.jingdong.service.JdProductService;
import cn.bfay.lion.util.DateUtils;
import cn.bfay.lion.util.NumberUtils;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

/**
 * MESSAGE.
 *
 * @author wangjiannan
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class JdProductServiceImpl extends AbstractService implements JdProductService {
    private final ProductInfoMapper productInfoMapper;
    private final ProductDetailInfoMapper productDetailInfoMapper;
    private final NoticeProducer noticeProducer;

    @Override
    public void doExecute() {
        LocalDateTime start = LocalDateTime.now();
        int pageNum = 1;
        long pageSize = 100L;
        int countAll = 0;
        int countError = 0;
        while (true) {
            Page<ProductInfo> pageParam = new Page<>(pageNum, pageSize);
            Page<ProductInfo> pageResult = productInfoMapper.selectPage(pageParam, new QueryWrapper<ProductInfo>().lambda().orderByAsc(ProductInfo::getId));
            List<ProductInfo> records = pageResult.getRecords();
            for (ProductInfo productInfo : records) {
                Long id = productInfo.getId();
                String skuid = productInfo.getSkuid();
                try {
                    JdProductInfo jdProductInfo = crawlJdProductInfo(skuid);
                    doProduct(jdProductInfo);
                    log.info("id={},skuid={}:处理结果-成功", id, skuid);
                } catch (Exception e) {
                    countError++;
                    log.error("id={},skuid={}:处理结果-失败", id, skuid, e);
                } finally {
                    countAll++;
                    try {
                        long sleepMillis = (long) (Math.random() * 250 + 250);
                        Thread.sleep(sleepMillis);
                        log.info("休眠时间{}ms", sleepMillis);
                    } catch (Exception e) {
                        log.error("", e);
                    }
                }
            }
            if (pageNum == pageResult.getPages()) {
                break;
            }
            pageNum++;
        }

        LocalDateTime end = LocalDateTime.now();
        log.info("本次处理商品耗时{}s,共处理商品{}个，错误{}个", DateUtils.secondsOfTimePeriod(start, end), countAll, countError);
    }

    private void doProduct(JdProductInfo jdProductInfo) {
        String skuid = jdProductInfo.getSkuid();
        if (jdProductInfo.getName() == null || jdProductInfo.getNormalPrice() == null) {
            return;
        }
        // 商品下架
        if (jdProductInfo.getNormalPrice() <= 0) {
            return;
        }

        ProductDetailInfo productDetailInfo = new ProductDetailInfo();
        BeanUtils.copyProperties(jdProductInfo, productDetailInfo);
        ProductInfo productInfo = new ProductInfo();
        BeanUtils.copyProperties(jdProductInfo, productInfo);
        // 处理数据
        if (!checkProductDetailInfo(productDetailInfo)) {
            productDetailInfo.setId(IdUtil.getSnowflake(0, 1).nextId());
            productDetailInfoMapper.insert(productDetailInfo);

            productInfo.setProductDetailInfoId(productDetailInfo.getId());
            productInfo.setValid(1);
            productInfoMapper.update(productInfo, new QueryWrapper<ProductInfo>().lambda()
                .eq(ProductInfo::getSkuid, skuid));

            // 设置商品最新的信息缓存
            RedisUtils.setValue(RedisKeyBuilder.generateRedisKey(RedisKeyTemplate.PRODUCT_DETAIL_INFO_TEMPLATE, skuid),
                productDetailInfo, RedisTime.MONTH);
            // 清除商品信息列表缓存
            RedisUtils.delete(RedisKeyBuilder.generateRedisKey(RedisKeyTemplate.PRODUCT_DETAIL_INFOS_TEMPLATE, skuid));
        }
        // 发送通知
        noticeProducer.sendNotice(productDetailInfo, productInfo);
    }

    /**
     * 检查当前新抓取的数据和上一次是否一样.
     *
     * @param productDetailInfo productDetailInfo
     * @return boolean
     */
    private boolean checkProductDetailInfo(ProductDetailInfo productDetailInfo) {
        String skuid = productDetailInfo.getSkuid();
        String redisKey = RedisKeyBuilder.generateRedisKey(RedisKeyTemplate.PRODUCT_DETAIL_INFO_TEMPLATE, skuid);

        ProductDetailInfo dbNewestProductDetailInfo;
        ProductDetailInfo cacheProductDetailInfo = RedisUtils.getValue(redisKey, ProductDetailInfo.class);
        if (cacheProductDetailInfo != null) {
            dbNewestProductDetailInfo = cacheProductDetailInfo;
        } else {
            dbNewestProductDetailInfo = productDetailInfoMapper.selectOne(new QueryWrapper<ProductDetailInfo>().lambda()
                .eq(ProductDetailInfo::getSkuid, skuid)
                .orderByDesc(ProductDetailInfo::getCreateTime)
                .last("limit 1"));
            // 不存在旧数据
            if (dbNewestProductDetailInfo == null) {
                return false;
            } else {
                RedisUtils.setValue(redisKey, dbNewestProductDetailInfo, RedisTime.MONTH);
            }
        }

        return NumberUtils.doubleEquals(dbNewestProductDetailInfo.getNormalPrice(), productDetailInfo.getNormalPrice())
            && NumberUtils.doubleEquals(dbNewestProductDetailInfo.getPlusPrice(), productDetailInfo.getPlusPrice())
            && NumberUtils
            .doubleEquals(dbNewestProductDetailInfo.getFinalNormalPrice(), productDetailInfo.getFinalNormalPrice())
            && NumberUtils
            .doubleEquals(dbNewestProductDetailInfo.getFinalPlusPrice(), productDetailInfo.getFinalPlusPrice());
    }
}
